﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using NHibernate.Event;
using NHibernate.Persister.Entity;
using ToyStore.Domain;
using ToyStore.Domain.Common;

namespace ToyStore.Infrastructure
{
    /// <summary>
    /// Klasa dzięki, które automatycznie możemy dodawać do obiekty właściwości podczas ich dodawania i uaktualniania.
    /// </summary>
    public class AuditEventListener : IPreInsertEventListener, IPreUpdateEventListener
    {
        /// <summary>
        /// obsługa zdarzenia insertu
        /// </summary>
        /// <param name="event">obiekt-parametr zdarzenia</param>
        /// <returns></returns>
        public bool OnPreInsert(PreInsertEvent @event)
        {
            if (@event == null)
                throw new ArgumentNullException("event");

        	BaseEntity baseEntity = @event.Entity as BaseEntity;
			if(baseEntity != null)
			{
				baseEntity.CreationDate = DateTime.Now;
				Set(() => baseEntity.CreationDate, @event.Persister, @event.State, baseEntity.CreationDate);
			}

            ProcessEntityBeforeInsert(@event.Entity as BaseEntity, @event.Persister.PropertyNames, @event.State, null);
            return false;
        }

        /// <summary>
        /// metoda realizująca audit encji
        /// </summary>
        /// <param name="baseEntity">obiekt encji</param>
        /// <param name="propertyNames"></param>
        /// <param name="state"></param>
        /// <param name="oldState"></param>
        internal virtual void ProcessEntityBeforeInsert(BaseEntity baseEntity, string[] propertyNames, object[] state, object[] oldState)
        {

        }

        /// <summary>
        /// obsługa zdarzenia Update'u
        /// </summary>
        /// <param name="event">parametr zdarzenia</param>
        /// <returns></returns>
        public bool OnPreUpdate(PreUpdateEvent @event)
        {
            if(@event == null)
                throw new ArgumentNullException("event");

			BaseEntity baseEntity = @event.Entity as BaseEntity;
			if (baseEntity != null)
			{
				baseEntity.ModificationDate = DateTime.Now;
				Set(() => baseEntity.ModificationDate, @event.Persister, @event.State, baseEntity.ModificationDate);
			}

            ProcessEntityBeforeInsert(@event.Entity as BaseEntity, @event.Persister.PropertyNames, @event.State, @event.OldState);
            return false;
        }

		/// <summary>
		/// Ustawienie danych w obiekcie ktory przechowuje dane do zapisania.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="propertyExpression"></param>
		/// <param name="persister"></param>
		/// <param name="state"></param>
		/// <param name="value"></param>
		[SuppressMessage("Microsoft.Performance", "CA1822: Mark members as static")]
		private void Set<T>(Expression<Func<T>> propertyExpression, IEntityPersister persister, object[] state, object value)
		{
			string propertyName = PropertySupport.ExtractPropertyName(propertyExpression);

			var index = Array.IndexOf(persister.PropertyNames, propertyName);
			if (index == -1)
				return;
			state[index] = value;
		}
    }
}
